#!/usr/bin/python
# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import annotations

DOCUMENTATION = r"""
module: oneview_ethernet_network
short_description: Manage OneView Ethernet Network resources
description:
  - Provides an interface to manage Ethernet Network resources. Can create, update, or delete.
requirements:
  - hpOneView >= 3.1.0
author:
  - Felipe Bulsoni (@fgbulsoni)
  - Thiago Miotto (@tmiotto)
  - Adriane Cardozo (@adriane-cardozo)
attributes:
  check_mode:
    support: none
  diff_mode:
    support: none
options:
  state:
    description:
      - Indicates the desired state for the Ethernet Network resource.
      - V(present) ensures data properties are compliant with OneView.
      - V(absent) removes the resource from OneView, if it exists.
      - V(default_bandwidth_reset) resets the network connection template to the default.
    type: str
    default: present
    choices: [present, absent, default_bandwidth_reset]
  data:
    description:
      - List with Ethernet Network properties.
    type: dict
    required: true
extends_documentation_fragment:
  - community.general.oneview
  - community.general.oneview.validateetag
  - community.general.attributes
"""

EXAMPLES = r"""
- name: Ensure that the Ethernet Network is present using the default configuration
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: present
    data:
      name: 'Test Ethernet Network'
      vlanId: '201'
  delegate_to: localhost

- name: Update the Ethernet Network changing bandwidth and purpose
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: present
    data:
      name: 'Test Ethernet Network'
      purpose: Management
      bandwidth:
        maximumBandwidth: 3000
        typicalBandwidth: 2000
  delegate_to: localhost

- name: Ensure that the Ethernet Network is present with name 'Renamed Ethernet Network'
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: present
    data:
      name: 'Test Ethernet Network'
      newName: 'Renamed Ethernet Network'
  delegate_to: localhost

- name: Ensure that the Ethernet Network is absent
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: absent
    data:
      name: 'New Ethernet Network'
  delegate_to: localhost

- name: Create Ethernet networks in bulk
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: present
    data:
      vlanIdRange: '1-10,15,17'
      purpose: General
      namePrefix: TestNetwork
      smartLink: false
      privateNetwork: false
      bandwidth:
        maximumBandwidth: 10000
        typicalBandwidth: 2000
  delegate_to: localhost

- name: Reset to the default network connection template
  community.general.oneview_ethernet_network:
    config: '/etc/oneview/oneview_config.json'
    state: default_bandwidth_reset
    data:
      name: 'Test Ethernet Network'
  delegate_to: localhost
"""

RETURN = r"""
ethernet_network:
  description: Has the facts about the Ethernet Networks.
  returned: On O(state=present). Can be null.
  type: dict

ethernet_network_bulk:
  description: Has the facts about the Ethernet Networks affected by the bulk insert.
  returned: When V(vlanIdRange) attribute is in O(data) argument. Can be null.
  type: dict

ethernet_network_connection_template:
  description: Has the facts about the Ethernet Network Connection Template.
  returned: On O(state=default_bandwidth_reset). Can be null.
  type: dict
"""

from ansible_collections.community.general.plugins.module_utils.oneview import (
    OneViewModuleBase,
    OneViewModuleResourceNotFound,
)


class EthernetNetworkModule(OneViewModuleBase):
    MSG_CREATED = "Ethernet Network created successfully."
    MSG_UPDATED = "Ethernet Network updated successfully."
    MSG_DELETED = "Ethernet Network deleted successfully."
    MSG_ALREADY_PRESENT = "Ethernet Network is already present."
    MSG_ALREADY_ABSENT = "Ethernet Network is already absent."

    MSG_BULK_CREATED = "Ethernet Networks created successfully."
    MSG_MISSING_BULK_CREATED = "Some missing Ethernet Networks were created successfully."
    MSG_BULK_ALREADY_EXIST = "The specified Ethernet Networks already exist."
    MSG_CONNECTION_TEMPLATE_RESET = "Ethernet Network connection template was reset to the default."
    MSG_ETHERNET_NETWORK_NOT_FOUND = "Ethernet Network was not found."

    RESOURCE_FACT_NAME = "ethernet_network"

    def __init__(self):
        argument_spec = dict(
            state=dict(type="str", default="present", choices=["absent", "default_bandwidth_reset", "present"]),
            data=dict(type="dict", required=True),
        )

        super().__init__(additional_arg_spec=argument_spec, validate_etag_support=True)

        self.resource_client = self.oneview_client.ethernet_networks

    def execute_module(self):
        changed, msg, ansible_facts, resource = False, "", {}, None

        if self.data.get("name"):
            resource = self.get_by_name(self.data["name"])

        if self.state == "present":
            if self.data.get("vlanIdRange"):
                return self._bulk_present()
            else:
                return self._present(resource)
        elif self.state == "absent":
            return self.resource_absent(resource)
        elif self.state == "default_bandwidth_reset":
            changed, msg, ansible_facts = self._default_bandwidth_reset(resource)
            return dict(changed=changed, msg=msg, ansible_facts=ansible_facts)

    def _present(self, resource):
        bandwidth = self.data.pop("bandwidth", None)
        scope_uris = self.data.pop("scopeUris", None)
        result = self.resource_present(resource, self.RESOURCE_FACT_NAME)

        if bandwidth:
            if self._update_connection_template(result["ansible_facts"]["ethernet_network"], bandwidth)[0]:
                result["changed"] = True
                result["msg"] = self.MSG_UPDATED

        if scope_uris is not None:
            result = self.resource_scopes_set(result, "ethernet_network", scope_uris)

        return result

    def _bulk_present(self):
        vlan_id_range = self.data["vlanIdRange"]
        result = dict(ansible_facts={})
        ethernet_networks = self.resource_client.get_range(self.data["namePrefix"], vlan_id_range)

        if not ethernet_networks:
            self.resource_client.create_bulk(self.data)
            result["changed"] = True
            result["msg"] = self.MSG_BULK_CREATED

        else:
            vlan_ids = self.resource_client.dissociate_values_or_ranges(vlan_id_range)
            for net in ethernet_networks[:]:
                vlan_ids.remove(net["vlanId"])

            if len(vlan_ids) == 0:
                result["msg"] = self.MSG_BULK_ALREADY_EXIST
                result["changed"] = False
            else:
                if len(vlan_ids) == 1:
                    self.data["vlanIdRange"] = f"{vlan_ids[0]}-{vlan_ids[0]}"
                else:
                    self.data["vlanIdRange"] = ",".join(str(s) for s in vlan_ids)

                self.resource_client.create_bulk(self.data)
                result["changed"] = True
                result["msg"] = self.MSG_MISSING_BULK_CREATED
        result["ansible_facts"]["ethernet_network_bulk"] = self.resource_client.get_range(
            self.data["namePrefix"], vlan_id_range
        )

        return result

    def _update_connection_template(self, ethernet_network, bandwidth):
        if "connectionTemplateUri" not in ethernet_network:
            return False, None

        connection_template = self.oneview_client.connection_templates.get(ethernet_network["connectionTemplateUri"])

        merged_data = connection_template.copy()
        merged_data.update({"bandwidth": bandwidth})

        if not self.compare(connection_template, merged_data):
            connection_template = self.oneview_client.connection_templates.update(merged_data)
            return True, connection_template
        else:
            return False, None

    def _default_bandwidth_reset(self, resource):
        if not resource:
            raise OneViewModuleResourceNotFound(self.MSG_ETHERNET_NETWORK_NOT_FOUND)

        default_connection_template = self.oneview_client.connection_templates.get_default()

        changed, connection_template = self._update_connection_template(
            resource, default_connection_template["bandwidth"]
        )

        return (
            changed,
            self.MSG_CONNECTION_TEMPLATE_RESET,
            dict(ethernet_network_connection_template=connection_template),
        )


def main():
    EthernetNetworkModule().run()


if __name__ == "__main__":
    main()
